package cn.demoncat.util.lang;

import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.function.Supplier;

/**
 * 单例工具
 *
 * @author 延晓磊
 * @since 2021年07月18日
 */
public class SingletonUtil {

	private static final MapCache<String, Object> POOL = new MapCache<>();

	/**
	 * 获得指定类的单例对象
	 *
	 * 1、对象存在于池中返回，否则创建，每次调用此方法获得的对象为同一个对象
	 * 2、单例针对的是类和参数，也就是说只有类、参数一致才会返回同一个对象
	 *
	 * @param clazz  类
	 * @param params 构造方法参数
	 *
	 * @return 单例对象
	 */
	public static <T> T get(Class<T> clazz, Object... params) {
		return get(buildKey(clazz.getName(), params), () -> ReflectClassUtil.instance(clazz, params));
	}

	/**
	 * 获得指定类的单例对象
	 *
	 * @param key      自定义键
	 * @param supplier 单例对象的创建函数
	 *
	 * @return 单例对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T get(String key, Supplier<T> supplier) {
		return (T) POOL.get(key, supplier::get);
	}

	/**
	 * 获得指定类的单例对象
	 *
	 * @param className 类名
	 * @param params    构造参数
	 *
	 * @return 单例对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T get(String className, Object... params) {
		return get((Class<T>)ReflectClassUtil.loadClass(className), params);
	}

	/**
	 * 将已有对象放入单例中，其Class做为键
	 *
	 * @param obj 对象
	 */
	public static void put(Object obj) {
		put(obj.getClass().getName(), obj);
	}

	/**
	 * 将已有对象放入单例中
	 *
	 * @param key 键
	 * @param obj 对象
	 */
	public static void put(String key, Object obj) {
		POOL.put(key, obj);
	}

	/**
	 * 移除指定Singleton对象
	 *
	 * @param clazz 类
	 */
	public static void remove(Class<?> clazz) {
		if (null != clazz) {
			remove(clazz.getName());
		}
	}

	/**
	 * 移除指定Singleton对象
	 *
	 * @param key 键
	 */
	public static void remove(String key) {
		POOL.remove(key);
	}

	/**
	 * 清除所有Singleton对象
	 */
	public static void destroy() {
		POOL.clear();
	}

	/**
	 * 构建key
	 *
	 * @param className 类名
	 * @param params    参数列表
	 * @return key
	 */
	private static String buildKey(String className, Object... params) {
		if (ArrayUtils.isEmpty(params)) {
			return className;
		}
		return className + StringConstant.HASH + StringUtils.join(params, StringConstant.BAR);
	}
}
